home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2006 May / PCpro_2006_05.ISO / files / mobile / fma-2.0-stable-setup.exe / {app} / source / uCrash.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2004-09-08  |  26.1 KB  |  767 lines

  1. {**************************************************************************************************}
  2. {                                                                                                  }
  3. { Project JEDI Code Library (JCL)                                                                  }
  4. {                                                                                                  }
  5. { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); }
  6. { you may not use this file except in compliance with the License. You may obtain a copy of the    }
  7. { License at http://www.mozilla.org/MPL/                                                           }
  8. {                                                                                                  }
  9. { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF   }
  10. { ANY KIND, either express or implied. See the License for the specific language governing rights  }
  11. { and limitations under the License.                                                               }
  12. {                                                                                                  }
  13. { The Original Code is ExceptDlg.pas.                                                              }
  14. {                                                                                                  }
  15. { The Initial Developer of the Original Code is documented in the accompanying                     }
  16. { help file JCL.chm. Portions created by these individuals are Copyright (C) of these individuals. }
  17. {                                                                                                  }
  18. {**************************************************************************************************}
  19. {                                                                                                  }
  20. { Sample Application exception dialog replacement                                                  }
  21. {                                                                                                  }
  22. { Last modified: April 1, 2003                                                                     }
  23. {                                                                                                  }
  24. {**************************************************************************************************}
  25.  
  26. unit uCrash;
  27.  
  28. {$I jcl.inc}
  29.  
  30. interface
  31.  
  32. uses
  33.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  34.   StdCtrls, ExtCtrls, JclDebug;
  35.  
  36. const
  37.   UM_CREATEDETAILS = WM_USER + $100;
  38.  
  39.   ReportToLogEnabled   = $00000001; // TExceptionDialog.Tag property
  40.   DisableTextScrollbar = $00000002; // TExceptionDialog.Tag property
  41.  
  42. type
  43.   TSimpleExceptionLog = class (TObject)
  44.   private
  45.     FLogFileHandle: THandle;
  46.     FLogFileName: string;
  47.     FLogWasEmpty: Boolean;
  48.     function GetLogOpen: Boolean;
  49.   protected
  50.     function CreateDefaultFileName: string;
  51.   public
  52.     constructor Create(const ALogFileName: string = '');
  53.     destructor Destroy; override;
  54.     procedure CloseLog;
  55.     procedure OpenLog;
  56.     procedure Write(const Text: string; Indent: Integer = 0); overload;
  57.     procedure Write(Strings: TStrings; Indent: Integer = 0); overload;
  58.     procedure WriteStamp(SeparatorLen: Integer = 0);
  59.     property LogFileName: string read FLogFileName;
  60.     property LogOpen: Boolean read GetLogOpen;
  61.   end;
  62.  
  63.   TExcDialogSystemInfo = (siStackList, siOsInfo, siModuleList, siActiveControls);
  64.   TExcDialogSystemInfos = set of TExcDialogSystemInfo;
  65.  
  66.   TExceptionDialog = class(TForm)
  67.     DetailsMemo: TMemo;
  68.     Bevel1: TBevel;
  69.     TextLabel: TMemo;
  70.     Label1: TLabel;
  71.     Panel1: TPanel;
  72.     OkBtn: TButton;
  73.     DetailsBtn: TButton;
  74.     procedure FormPaint(Sender: TObject);
  75.     procedure FormCreate(Sender: TObject);
  76.     procedure FormShow(Sender: TObject);
  77.     procedure DetailsBtnClick(Sender: TObject);
  78.     procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  79.     procedure FormDestroy(Sender: TObject);
  80.     procedure FormResize(Sender: TObject);
  81.   private
  82.     FDetailsVisible: Boolean;
  83.     FIsMainThead: Boolean;
  84.     FLastActiveControl: TWinControl;
  85.     FNonDetailsHeight: Integer;
  86.     FFullHeight: Integer;
  87.     FSimpleLog: TSimpleExceptionLog;
  88.     procedure CreateDetails;
  89.     function GetReportAsText: string;
  90.     procedure ReportToLog;
  91.     procedure SetDetailsVisible(const Value: Boolean);
  92.     procedure UMCreateDetails(var Message: TMessage); message UM_CREATEDETAILS;
  93.   protected
  94.     procedure AfterCreateDetails; dynamic;
  95.     procedure BeforeCreateDetails; dynamic;
  96.     procedure CreateDetailInfo; dynamic;
  97.     procedure CreateReport(const SystemInfo: TExcDialogSystemInfos);
  98.     function ReportMaxColumns: Integer; virtual;
  99.     function ReportNewBlockDelimiterChar: Char; virtual;
  100.     procedure NextDetailBlock;
  101.     procedure UpdateTextLabelScrollbars;
  102.   public
  103.     procedure CopyReportToClipboard;
  104.     class procedure ExceptionHandler(Sender: TObject; E: Exception);
  105.     class procedure ExceptionThreadHandler(Thread: TJclDebugThread);
  106.     class procedure ShowException(E: Exception; Thread: TJclDebugThread);
  107.     property DetailsVisible: Boolean read FDetailsVisible write SetDetailsVisible;
  108.     property ReportAsText: string read GetReportAsText;
  109.     property SimpleLog: TSimpleExceptionLog read FSimpleLog;
  110.   end;
  111.  
  112.   TExceptionDialogClass = class of TExceptionDialog;
  113.  
  114. var
  115.   ExceptionDialogClass: TExceptionDialogClass = TExceptionDialog;
  116.  
  117. implementation
  118.  
  119. {$R *.DFM}
  120.  
  121. uses
  122.   ClipBrd, Math,
  123.   JclBase, JclFileUtils, JclHookExcept, JclPeImage, JclStrings, JclSysInfo, JclSysUtils;
  124.  
  125. resourcestring
  126.   RsAppError = '%s - application error';
  127.   RsExceptionClass = 'Exception class: %s';
  128.   RsExceptionMessage = 'Exception message: %s';
  129.   RsExceptionAddr = 'Exception address: %p';
  130.   RsStackList = 'Stack list, generated %s';
  131.   RsModulesList = 'List of loaded modules:';
  132.   RsOSVersion = 'System   : %s %s, Version: %d.%d, Build: %x, "%s"';
  133.   RsProcessor = 'Processor: %s, %s, %d MHz %s%s';
  134.   RsScreenRes = 'Display  : %dx%d pixels, %d bpp';
  135.   RsActiveControl = 'Active Controls hiearchy:';
  136.   RsThread = 'Thread: %s';
  137.   RsMissingVersionInfo = '(no version info)';
  138.  
  139. var
  140.   ExceptionDialog: TExceptionDialog;
  141.  
  142. //==================================================================================================
  143. // Helper routines
  144. //==================================================================================================
  145.  
  146. function GetBPP: Integer;
  147. var
  148.   DC: HDC;
  149. begin
  150.   DC := GetDC(0);
  151.   Result := GetDeviceCaps(DC, BITSPIXEL) * GetDeviceCaps(DC, PLANES);
  152.   ReleaseDC(0, DC);
  153. end;
  154.  
  155. //--------------------------------------------------------------------------------------------------
  156.  
  157. function SortModulesListByAddressCompare(List: TStringList; Index1, Index2: Integer): Integer;
  158. begin
  159.   Result := Integer(List.Objects[Index1]) - Integer(List.Objects[Index2]);
  160. end;
  161.  
  162. //==================================================================================================
  163. // TApplication.HandleException method code hooking for exceptions from DLLs
  164. //==================================================================================================
  165.  
  166. // We need to catch the last line of TApplication.HandleException method:
  167. // [...]
  168. //   end else
  169. //    SysUtils.ShowException(ExceptObject, ExceptAddr);
  170. // end;
  171.  
  172. procedure HookShowException(ExceptObject: TObject; ExceptAddr: Pointer);
  173. begin
  174.   if JclValidateModuleAddress(ExceptAddr) and (ExceptObject.InstanceSize >= Exception.InstanceSize) then
  175.     TExceptionDialog.ExceptionHandler(nil, Exception(ExceptObject))
  176.   else
  177.     SysUtils.ShowException(ExceptObject, ExceptAddr);
  178. end;
  179.  
  180. //--------------------------------------------------------------------------------------------------
  181.  
  182. function HookTApplicationHandleException: Boolean;
  183. const
  184.   CallOffset      = $86;
  185.   CallOffsetDebug = $94;
  186. type
  187.   PCALLInstruction = ^TCALLInstruction;
  188.   TCALLInstruction = packed record
  189.     Call: Byte;
  190.     Address: Integer;
  191.   end;
  192. var
  193.   TApplicationHandleExceptionAddr, SysUtilsShowExceptionAddr: Pointer;
  194.   CALLInstruction: TCALLInstruction;
  195.   CallAddress: Pointer;
  196.   NW: DWORD;
  197.  
  198.   function CheckAddressForOffset(Offset: Cardinal): Boolean;
  199.   begin
  200.     try
  201.       CallAddress := Pointer(Cardinal(TApplicationHandleExceptionAddr) + Offset);
  202.       CALLInstruction.Call := $E8;
  203.       Result := PCALLInstruction(CallAddress)^.Call = CALLInstruction.Call;
  204.       if Result then
  205.       begin
  206.         if IsCompiledWithPackages then
  207.           Result := PeMapImgResolvePackageThunk(Pointer(Integer(CallAddress) + Integer(PCALLInstruction(CallAddress)^.Address) + SizeOf(CALLInstruction))) = SysUtilsShowExceptionAddr
  208.         else
  209.           Result := PCALLInstruction(CallAddress)^.Address = Integer(SysUtilsShowExceptionAddr) - Integer(CallAddress) - SizeOf(CALLInstruction);
  210.       end;
  211.     except
  212.       Result := False;
  213.     end;    
  214.   end;
  215.  
  216. begin
  217.   TApplicationHandleExceptionAddr := PeMapImgResolvePackageThunk(@TApplication.HandleException);
  218.   SysUtilsShowExceptionAddr := PeMapImgResolvePackageThunk(@SysUtils.ShowException);
  219.   Result := CheckAddressForOffset(CallOffset) or CheckAddressForOffset(CallOffsetDebug);
  220.   if Result then
  221.   begin
  222.     CALLInstruction.Address := Integer(@HookShowException) - Integer(CallAddress) - SizeOf(CALLInstruction);
  223.     Result := WriteProcessMemory(GetCurrentProcess, CallAddress, @CALLInstruction, SizeOf(CALLInstruction), NW);
  224.     if Result then
  225.       FlushInstructionCache(GetCurrentProcess, CallAddress, SizeOf(CALLInstruction));
  226.   end;
  227. end;
  228.  
  229. //==================================================================================================
  230. // TSimpleExceptionLog
  231. //==================================================================================================
  232.  
  233. procedure TSimpleExceptionLog.CloseLog;
  234. begin
  235.   if LogOpen then
  236.   begin
  237.     CloseHandle(FLogFileHandle);
  238.     FLogFileHandle := INVALID_HANDLE_VALUE;
  239.     FLogWasEmpty := False;
  240.   end;
  241. end;
  242.  
  243. //--------------------------------------------------------------------------------------------------
  244.  
  245. constructor TSimpleExceptionLog.Create(const ALogFileName: string);
  246. begin
  247.   if ALogFileName = '' then
  248.     FLogFileName := CreateDefaultFileName
  249.   else
  250.     FLogFileName := ALogFileName;
  251.   FLogFileHandle := INVALID_HANDLE_VALUE;
  252. end;
  253.  
  254. //--------------------------------------------------------------------------------------------------
  255.  
  256. function TSimpleExceptionLog.CreateDefaultFileName: string;
  257. begin
  258.   Result := PathExtractFileDirFixed(ParamStr(0)) + PathExtractFileNameNoExt(ParamStr(0)) + '_Err.log';
  259. end;
  260.  
  261. //--------------------------------------------------------------------------------------------------
  262.  
  263. destructor TSimpleExceptionLog.Destroy;
  264. begin
  265.   CloseLog;
  266.   inherited;
  267. end;
  268.  
  269. //--------------------------------------------------------------------------------------------------
  270.  
  271. function TSimpleExceptionLog.GetLogOpen: Boolean;
  272. begin
  273.   Result := FLogFileHandle <> INVALID_HANDLE_VALUE;
  274. end;
  275.  
  276. //--------------------------------------------------------------------------------------------------
  277.  
  278. procedure TSimpleExceptionLog.OpenLog;
  279. begin
  280.   if not LogOpen then
  281.   begin
  282.     FLogFileHandle := CreateFile(PChar(FLogFileName), GENERIC_WRITE, FILE_SHARE_READ, nil,
  283.       OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  284.     if LogOpen then
  285.       FLogWasEmpty := SetFilePointer(FLogFileHandle, 0, nil, FILE_END) = 0;
  286.   end
  287.   else
  288.     FLogWasEmpty := False;
  289. end;
  290.  
  291. //--------------------------------------------------------------------------------------------------
  292.  
  293. procedure TSimpleExceptionLog.Write(const Text: string; Indent: Integer);
  294. var
  295.   S: string;
  296.   SL: TStringList;
  297.   I: Integer;
  298. begin
  299.   if LogOpen then
  300.   begin
  301.     SL := TStringList.Create;
  302.     try
  303.       SL.Text := Text;
  304.       for I := 0 to SL.Count - 1 do
  305.       begin
  306.         S := StringOfChar(' ', Indent) + StrEnsureSuffix(AnsiCrLf, TrimRight(SL[I]));
  307.         FileWrite(Integer(FLogFileHandle), Pointer(S)^, Length(S));
  308.       end;
  309.     finally
  310.       SL.Free;
  311.     end;
  312.   end;
  313. end;
  314.  
  315. //--------------------------------------------------------------------------------------------------
  316.  
  317. procedure TSimpleExceptionLog.Write(Strings: TStrings; Indent: Integer);
  318. var
  319.   I: Integer;
  320. begin
  321.   for I := 0 to Strings.Count - 1 do
  322.     Write(Strings[I], Indent);
  323. end;
  324.  
  325. //--------------------------------------------------------------------------------------------------
  326.  
  327. procedure TSimpleExceptionLog.WriteStamp(SeparatorLen: Integer);
  328. begin
  329.   if SeparatorLen = 0 then
  330.     SeparatorLen := 100;
  331.   SeparatorLen := Max(SeparatorLen, 20);  
  332.   OpenLog;
  333.   if not FLogWasEmpty then
  334.     Write(AnsiCrLf);
  335.   Write(StrRepeat('=', SeparatorLen));
  336.   Write(Format('= %-*s =', [SeparatorLen - 4, DateTimeToStr(Now)]));
  337.   Write(StrRepeat('=', SeparatorLen));
  338. end;
  339.  
  340. //==================================================================================================
  341. // Exception dialog
  342. //==================================================================================================
  343.  
  344. var
  345.   ExceptionShowing: Boolean;
  346.  
  347. { TExceptionDialog }
  348.  
  349. procedure TExceptionDialog.AfterCreateDetails;
  350. var
  351.   i,j: integer;
  352. begin
  353.   i := Canvas.TextWidth(TextLabel.Text) + TextLabel.Left + 32;
  354.   j := Round(Screen.Width * 541 / 1000); // maximum widht
  355.   if i < Constraints.MinWidth then i := Constraints.MinWidth
  356.     else if i > j then i := j;
  357.   Left := (Screen.Width - i) div 2;
  358.   Width := i;
  359.   case TextLabel.Lines.Count of
  360.     1: begin
  361.          TextLabel.Height := 44;
  362.          TextLabel.Top := 16;
  363.        end;
  364.     2: begin
  365.          TextLabel.Height := 48;
  366.          TextLabel.Top := 12;
  367.        end;
  368.   else begin
  369.          TextLabel.Height := 52;
  370.          TextLabel.Top := 8;
  371.        end;
  372.   end;
  373.   TextLabel.Visible := True;
  374. end;
  375.  
  376. //--------------------------------------------------------------------------------------------------
  377.  
  378. procedure TExceptionDialog.BeforeCreateDetails;
  379. begin
  380.   TextLabel.Visible := False;
  381. end;
  382.  
  383. //--------------------------------------------------------------------------------------------------
  384.  
  385. procedure TExceptionDialog.CopyReportToClipboard;
  386. begin
  387.   ClipBoard.AsText := ReportAsText;
  388. end;
  389.  
  390. //--------------------------------------------------------------------------------------------------
  391.  
  392. procedure TExceptionDialog.CreateDetailInfo;
  393. begin
  394.   CreateReport([siStackList, siOsInfo, siModuleList, siActiveControls]);
  395. end;
  396.  
  397. //--------------------------------------------------------------------------------------------------
  398.  
  399. procedure TExceptionDialog.CreateDetails;
  400. begin
  401.   Screen.Cursor := crHourGlass;
  402.   DetailsMemo.Lines.BeginUpdate;
  403.   try
  404.     CreateDetailInfo;
  405.     ReportToLog;
  406.     DetailsMemo.SelStart := 0;
  407.     SendMessage(DetailsMemo.Handle, EM_SCROLLCARET, 0, 0);
  408.     AfterCreateDetails;
  409.   finally
  410.     DetailsMemo.Lines.EndUpdate;
  411.     OkBtn.Enabled := True;
  412.     DetailsBtn.Enabled := True;
  413.     OkBtn.SetFocus;
  414.     Screen.Cursor := crDefault;
  415.   end;
  416. end;
  417.  
  418. //--------------------------------------------------------------------------------------------------
  419.  
  420. procedure TExceptionDialog.CreateReport(const SystemInfo: TExcDialogSystemInfos);
  421. const
  422.   MMXText: array[Boolean] of PChar = ('', 'MMX');
  423.   FDIVText: array[Boolean] of PChar = (' [FDIV Bug]', '');
  424. var
  425.   SL: TStringList;
  426.   I: Integer;
  427.   ModuleName: TFileName;
  428.   CpuInfo: TCpuInfo;
  429.   C: TWinControl;
  430.   NtHeaders: PImageNtHeaders;
  431.   ModuleBase: Cardinal;
  432.   ImageBaseStr: string;
  433.   StackList: TJclStackInfoList;
  434. begin
  435.   SL := TStringList.Create;
  436.   try
  437.     // Stack list
  438.     if siStackList in SystemInfo then
  439.     begin
  440.       StackList := JclLastExceptStackList;
  441.       if Assigned(StackList) then
  442.       begin
  443.         DetailsMemo.Lines.Add(Format(RsStackList, [DateTimeToStr(StackList.TimeStamp)]));
  444.         StackList.AddToStrings(DetailsMemo.Lines, False, True, True);
  445.         NextDetailBlock;
  446.       end;
  447.     end;
  448.     // System and OS information
  449.     if siOsInfo in SystemInfo then
  450.     begin
  451.       DetailsMemo.Lines.Add(Format(RsOSVersion, [GetWindowsVersionString, NtProductTypeString,
  452.         Win32MajorVersion, Win32MinorVersion, Win32BuildNumber, Win32CSDVersion]));
  453.       GetCpuInfo(CpuInfo);
  454.       with CpuInfo do
  455.         DetailsMemo.Lines.Add(Format(RsProcessor, [Manufacturer, CpuName,
  456.           RoundFrequency(FrequencyInfo.NormFreq),
  457.           MMXText[MMX], FDIVText[IsFDIVOK]]));
  458.       DetailsMemo.Lines.Add(Format(RsScreenRes, [Screen.Width, Screen.Height, GetBPP]));
  459.       NextDetailBlock;
  460.     end;
  461.     // Modules list
  462.     if (siModuleList in SystemInfo) and LoadedModulesList(SL, GetCurrentProcessId) then
  463.     begin
  464.       DetailsMemo.Lines.Add(RsModulesList);
  465.       SL.CustomSort(SortModulesListByAddressCompare);
  466.       for I := 0 to SL.Count - 1 do
  467.       begin
  468.         ModuleName := SL[I];
  469.         ModuleBase := Cardinal(SL.Objects[I]);
  470.         DetailsMemo.Lines.Add(Format('[%.8x] %s', [ModuleBase, ModuleName]));
  471.         NtHeaders := PeMapImgNtHeaders(Pointer(ModuleBase));
  472.         if (NtHeaders <> nil) and (NtHeaders^.OptionalHeader.ImageBase <> ModuleBase) then
  473.           ImageBaseStr := Format('<%.8x> ', [NtHeaders^.OptionalHeader.ImageBase])
  474.         else
  475.           ImageBaseStr := StrRepeat(' ', 11);
  476.         if VersionResourceAvailable(ModuleName) then
  477.           with TJclFileVersionInfo.Create(ModuleName) do
  478.           try
  479.             DetailsMemo.Lines.Add(ImageBaseStr + BinFileVersion + ' - ' + FileVersion);
  480.             if FileDescription <> '' then
  481.               DetailsMemo.Lines.Add(StrRepeat(' ', 11) + FileDescription);
  482.           finally
  483.             Free;
  484.           end
  485.         else
  486.           DetailsMemo.Lines.Add(ImageBaseStr + RsMissingVersionInfo);
  487.       end;
  488.       NextDetailBlock;
  489.     end;
  490.     // Active controls
  491.     if (siActiveControls in SystemInfo) and (FLastActiveControl <> nil) then
  492.     begin
  493.       DetailsMemo.Lines.Add(RsActiveControl);
  494.       C := FLastActiveControl;
  495.       while C <> nil do
  496.       begin
  497.         DetailsMemo.Lines.Add(Format('%s "%s"', [C.ClassName, C.Name]));
  498.         C := C.Parent;
  499.       end;
  500.       NextDetailBlock;
  501.     end;
  502.   finally
  503.     SL.Free;
  504.   end;
  505. end;
  506.  
  507. //--------------------------------------------------------------------------------------------------
  508.  
  509. procedure TExceptionDialog.DetailsBtnClick(Sender: TObject);
  510. begin
  511.   DetailsVisible := not DetailsVisible;
  512. end;
  513.  
  514. //--------------------------------------------------------------------------------------------------
  515.  
  516. class procedure TExceptionDialog.ExceptionHandler(Sender: TObject; E: Exception);
  517. begin
  518.   if ExceptionShowing then
  519.     Application.ShowException(E)
  520.   else
  521.   begin
  522.     ExceptionShowing := True;
  523.     try
  524.       ShowException(E, nil);
  525.     finally
  526.       ExceptionShowing := False;
  527.     end;
  528.   end;
  529. end;
  530.  
  531. //--------------------------------------------------------------------------------------------------
  532.  
  533. class procedure TExceptionDialog.ExceptionThreadHandler(Thread: TJclDebugThread);
  534. begin
  535.   if ExceptionShowing then
  536.     Application.ShowException(Thread.SyncException)
  537.   else
  538.   begin
  539.     ExceptionShowing := True;
  540.     try
  541.       ShowException(Thread.SyncException, Thread);
  542.     finally
  543.       ExceptionShowing := False;
  544.     end;
  545.   end;
  546. end;
  547.  
  548. //--------------------------------------------------------------------------------------------------
  549.  
  550. procedure TExceptionDialog.FormCreate(Sender: TObject);
  551. begin
  552.   FSimpleLog := TSimpleExceptionLog.Create;
  553.   FFullHeight := ClientHeight;
  554.   DetailsVisible := False;
  555.   Caption := Format(RsAppError, [Application.Title]);
  556. end;
  557.  
  558. //--------------------------------------------------------------------------------------------------
  559.  
  560. procedure TExceptionDialog.FormDestroy(Sender: TObject);
  561. begin
  562.   FreeAndNil(FSimpleLog);
  563. end;
  564.  
  565. //--------------------------------------------------------------------------------------------------
  566.  
  567. procedure TExceptionDialog.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  568. begin
  569.   if (Key = Ord('C')) and (ssCtrl in Shift) then
  570.   begin
  571.     CopyReportToClipboard;
  572.     //MessageBeep(MB_OK);
  573.   end;
  574. end;
  575.  
  576. //--------------------------------------------------------------------------------------------------
  577.  
  578. procedure TExceptionDialog.FormPaint(Sender: TObject);
  579. begin
  580.   DrawIcon(Canvas.Handle, TextLabel.Left - GetSystemMetrics(SM_CXICON) - 15,
  581.     8{TextLabel.Top}, LoadIcon(0, IDI_ERROR));
  582. end;
  583.  
  584. //--------------------------------------------------------------------------------------------------
  585.  
  586. procedure TExceptionDialog.FormResize(Sender: TObject);
  587. begin
  588.   UpdateTextLabelScrollbars;
  589. end;
  590.  
  591. //--------------------------------------------------------------------------------------------------
  592.  
  593. procedure TExceptionDialog.FormShow(Sender: TObject);
  594. begin
  595.   BeforeCreateDetails;
  596.   MessageBeep(MB_ICONERROR);
  597.   if FIsMainThead and (GetWindowThreadProcessId(Handle, nil) = MainThreadID) then
  598.     PostMessage(Handle, UM_CREATEDETAILS, 0, 0)
  599.   else
  600.     CreateDetails;
  601. end;
  602.  
  603. //--------------------------------------------------------------------------------------------------
  604.  
  605. function TExceptionDialog.GetReportAsText: string;
  606. begin
  607.   Result := StrEnsureSuffix(AnsiCrLf, TextLabel.Text) + AnsiCrLf + DetailsMemo.Text;
  608. end;
  609.  
  610. //--------------------------------------------------------------------------------------------------
  611.  
  612. procedure TExceptionDialog.NextDetailBlock;
  613. begin
  614.   DetailsMemo.Lines.Add(StrRepeat(ReportNewBlockDelimiterChar, ReportMaxColumns));
  615. end;
  616.  
  617. //--------------------------------------------------------------------------------------------------
  618.  
  619. function TExceptionDialog.ReportMaxColumns: Integer;
  620. begin
  621.   Result := 100;
  622. end;
  623.  
  624. //--------------------------------------------------------------------------------------------------
  625.  
  626. function TExceptionDialog.ReportNewBlockDelimiterChar: Char;
  627. begin
  628.   Result := '-';
  629. end;
  630.  
  631. //--------------------------------------------------------------------------------------------------
  632.  
  633. procedure TExceptionDialog.ReportToLog;
  634. begin
  635.   if Tag and ReportToLogEnabled <> 0 then
  636.   begin
  637.     FSimpleLog.WriteStamp(ReportMaxColumns);
  638.     try
  639.       FSimpleLog.Write(ReportAsText);
  640.     finally
  641.       FSimpleLog.CloseLog;
  642.     end;
  643.   end;
  644. end;
  645.  
  646. //--------------------------------------------------------------------------------------------------
  647.  
  648. procedure TExceptionDialog.SetDetailsVisible(const Value: Boolean);
  649. var
  650.   DetailsCaption: string;
  651. begin
  652.   FDetailsVisible := Value;
  653.   DetailsCaption := Trim(StrRemoveChars(DetailsBtn.Caption, ['<', '>']));
  654.   if Value then
  655.   begin
  656.     Constraints.MinHeight := FNonDetailsHeight + 100;
  657.     Constraints.MaxHeight := Screen.Height;
  658.     DetailsCaption := '<< ' + DetailsCaption;
  659.     ClientHeight := FFullHeight;
  660.     DetailsMemo.Height := FFullHeight - DetailsMemo.Top - 3;
  661.   end
  662.   else
  663.   begin
  664.     FFullHeight := ClientHeight;
  665.     DetailsCaption := DetailsCaption + ' >>';
  666.     if FNonDetailsHeight = 0 then
  667.     begin
  668.       ClientHeight := Bevel1.Top;
  669.       FNonDetailsHeight := Height;
  670.     end
  671.     else
  672.       Height := FNonDetailsHeight;
  673.     Constraints.MinHeight := FNonDetailsHeight;
  674.     Constraints.MaxHeight := FNonDetailsHeight
  675.   end;
  676.   DetailsBtn.Caption := DetailsCaption;
  677.   DetailsMemo.Enabled := Value;
  678. end;
  679.  
  680. //--------------------------------------------------------------------------------------------------
  681.  
  682. class procedure TExceptionDialog.ShowException(E: Exception; Thread: TJclDebugThread);
  683. begin
  684.   if ExceptionDialog = nil then
  685.     ExceptionDialog := ExceptionDialogClass.Create(Application);
  686.   try
  687.     with ExceptionDialog do
  688.     begin
  689.       FIsMainThead := (GetCurrentThreadId = MainThreadID);
  690.       FLastActiveControl := Screen.ActiveControl;
  691.       TextLabel.Text := AdjustLineBreaks(StrEnsureSuffix('.', E.Message));
  692.       UpdateTextLabelScrollbars;
  693.       DetailsMemo.Lines.Add(Format(RsExceptionClass, [E.ClassName]));
  694.       DetailsMemo.Lines.Add(Format(RsExceptionMessage, [E.Message]));      
  695.       if Thread = nil then
  696.         DetailsMemo.Lines.Add(Format(RsExceptionAddr, [ExceptAddr]))
  697.       else
  698.         DetailsMemo.Lines.Add(Format(RsThread, [Thread.ThreadInfo]));
  699.       NextDetailBlock;
  700.       ShowModal;
  701.     end;
  702.   finally
  703.     FreeAndNil(ExceptionDialog);
  704.   end;
  705. end;
  706.  
  707. //--------------------------------------------------------------------------------------------------
  708.  
  709. procedure TExceptionDialog.UMCreateDetails(var Message: TMessage);
  710. begin
  711.   Update;
  712.   CreateDetails;
  713. end;
  714.  
  715. //--------------------------------------------------------------------------------------------------
  716.  
  717. procedure TExceptionDialog.UpdateTextLabelScrollbars;
  718. begin
  719.   if Tag and DisableTextScrollbar = 0 then
  720.   begin
  721.     Canvas.Font := TextLabel.Font;
  722.     if TextLabel.Lines.Count * Canvas.TextHeight('Wg') > TextLabel.ClientHeight then
  723.       TextLabel.ScrollBars := ssVertical
  724.     else
  725.       TextLabel.ScrollBars := ssNone;
  726.    end;   
  727. end;
  728.  
  729. //==================================================================================================
  730. // Exception handler initialization code
  731. //==================================================================================================
  732.  
  733. procedure InitializeHandler;
  734. begin
  735.   JclStackTrackingOptions := JclStackTrackingOptions + [stRawMode];
  736.   {$IFNDEF HOOK_DLL_EXCEPTIONS}
  737.   JclStackTrackingOptions := JclStackTrackingOptions + [stStaticModuleList];
  738.   {$ENDIF HOOK_DLL_EXCEPTIONS}
  739.   JclDebugThreadList.OnSyncException := TExceptionDialog.ExceptionThreadHandler;
  740.   JclStartExceptionTracking;
  741.   {$IFDEF HOOK_DLL_EXCEPTIONS}
  742.   if HookTApplicationHandleException then
  743.     JclTrackExceptionsFromLibraries;
  744.   {$ENDIF HOOK_DLL_EXCEPTIONS}
  745.   Application.OnException := TExceptionDialog.ExceptionHandler;
  746. end;
  747.  
  748. //--------------------------------------------------------------------------------------------------
  749.  
  750. procedure UnInitializeHandler;
  751. begin
  752.   Application.OnException := nil;
  753.   JclDebugThreadList.OnSyncException := nil;
  754.   JclUnhookExceptions;
  755.   JclStopExceptionTracking;
  756. end;
  757.  
  758. //--------------------------------------------------------------------------------------------------
  759.  
  760. initialization
  761.   InitializeHandler;
  762.  
  763. finalization
  764.   UnInitializeHandler;
  765.  
  766. end.
  767.